<script lang="ts" setup>
import type {
  ActReProcdefVO,
  DynamicParametersVO,
  OperateActReProcdefReq,
  QueryActReProcdefReq,
  QueryDynamicParametersReq,
  QueryImageOrXmlReq,
  StartProcessDynamicParametersReq,
  StartProcessReq,
} from '@/api/actReProcdef/actReProcdefType'
import {
  operateActReProcdef,
  pageActReProcdef,
  queryDynamicParameters,
  queryImage,
  queryXml,
  startProcess,
} from '@/api/actReProcdef/actReProcdefApi'

import useLayoutStore from '@/store/modules/setting'
import { ElMessage } from 'element-plus'
// 引入 xml 美化工具
import { PrettyXml } from 'pretty-xml-vue3'
import { onMounted, reactive, ref } from 'vue'

// 使用layout组件小仓库
const layoutStore = useLayoutStore()

// 定义响应式数据 processName 收集查询条件的流程名称
const processName = ref('')
// 定义响应式数据 currentPage 收集当前页码
const currentPage = ref<number>(1)
// 定义响应式数据 pageSize 收集每页显示的条数
const pageSize = ref<number>(10)
// 定义响应式数据 total 收集总数据条数
const total = ref<number>(0)
// 定义响应式数据 actReDeploymentList 表示流程定义列表
const actReProcdefList = ref<ActReProcdefVO[]>([])
// 表格列定义
const tableColumns = [
  { label: '#', type: 'index', align: 'center', width: '50px' },
  { label: 'ID', prop: 'id', align: 'center' },
  { label: '定义类型', prop: 'category', align: 'center' },
  { label: '定义名称', prop: 'name', align: 'center' },
  { label: '定义Key', prop: 'key', align: 'center' },
  { label: '定义版本', prop: 'version', align: 'center' },
  { label: '部署ID', prop: 'deploymentId', align: 'center' },
  { label: '资源名称', prop: 'resourceName', align: 'center' },
  { label: '图片名称', prop: 'dgrmResourceName', align: 'center' },
  { label: '定义状态', prop: 'suspensionState', align: 'center' },
  { label: '操作', align: 'center', width: '200px' },
]

// 定义响应式数据 showImage 表示是否显示流程定义的图片对话框
const showImage = ref(false)

// 定义响应式数据 imageData 表示流程定义的图片数据
const imageData = ref('')

// 定义响应式数据 showXml 表示是否显示流程定义的XML对话框
const showXml = ref(false)

// 定义响应式数据 xmlData 表示流程定义的XML数据
const xmlData = ref('')

// 定义响应式数据 showStart 表示是否显示流程定义的启动对话框
const showStart = ref(false)
// 定义响应式数据 DynamicParametersData 表示动态参数数据
const dynamicParametersData = ref<DynamicParametersVO[]>([])
// 定义 动态参数表单数据
const dynamicForm = reactive<{ data: DynamicParametersVO[] }>({
  data: [], // 显式初始化 data 属性为一个空数组
})
// 收集 动态参数表单 实例
const dynamicFormRef = ref()
// 定义响应式数据收集 processDefinitionId 表示流程定义ID
const processDefinitionId = ref('')

/**
 * onMounted 生命周期钩子函数
 *
 * 在组件挂载完成后执行以下操作：
 * 1. 初始化分页参数和流程名称。
 * 2. 调用函数获取流程定义的分页数据。
 *
 * @param {Function} onMounted - Vue 的生命周期钩子函数，无需传参。
 * @returns {void} 无返回值。
 */
onMounted(() => {
  // 初始化分页参数：当前页为第一页，每页显示10条数据
  currentPage.value = 1
  pageSize.value = 10

  // 初始化流程名称为空字符串
  processName.value = ''

  // 调用函数获取流程定义的分页数据
  getActReProcdefPageData()
})

/**
 * 获取流程定义分页数据的异步函数。
 *
 * @function getActReProcdefPageData
 * @description 该函数用于组装分页查询参数，调用后端接口获取流程定义数据，并处理查询结果或异常。
 *
 * @param {void} - 无参数。
 * @returns {Promise<void>} - 无返回值，但会更新流程定义列表和总记录数，或在失败时显示错误消息。
 */
async function getActReProcdefPageData() {
  try {
    // 组装分页查询参数，包含部署名称和分页信息
    const queryParams: QueryActReProcdefReq = {
      processName: processName.value,
      pageReq: {
        current: currentPage.value,
        size: pageSize.value,
      },
    }

    // 调用分页查询接口，获取流程定义数据
    const result: any = await pageActReProcdef(queryParams)

    // 判断查询结果是否成功，成功则更新流程列表和总记录数
    if (result.success && result.code === 200) {
      actReProcdefList.value = result.data
      total.value = result.total
    }
  }
  catch (error) {
    // 捕获异常并提取错误信息
    let errorMessage = '未知错误'
    if (error instanceof Error) {
      errorMessage = error.message
    }

    // 显示异常错误消息
    ElMessage({
      message: `查询失败: ${errorMessage || '未知错误'}`,
      type: 'error',
    })
  }
}

// 重置查询条件
function resetQuery() {
  layoutStore.refresh = !layoutStore.refresh
}

/**
 * 处理页面数据的函数。
 * 该函数调用获取流程定义页面数据的方法。
 *
 * @function handerPageData
 */
function handerPageData() {
  // 调用获取流程定义页面数据的函数
  getActReProcdefPageData()
}

/**
 * 异步函数：onOperate
 *
 * 该函数用于对流程定义进行操作（如挂起或激活），并根据操作结果更新页面状态。
 *
 * @param {ActReProcdefVO} row - 流程定义对象，包含流程定义的详细信息，例如 ID 和挂起状态。
 *                                - `id`: 流程定义的唯一标识符。
 *                                - `suspensionState`: 流程定义的挂起状态，1 表示激活，2 表示挂起。
 * @returns {Promise<void>} - 无返回值，函数执行完成后会根据操作结果更新页面状态或显示提示信息。
 */
async function onOperate(row: ActReProcdefVO) {
  try {
    // 组装操作参数，包括流程定义 ID 和操作类型
    const param: OperateActReProcdefReq = {
      processDefinitionId: row.id,
      operateType: row.suspensionState === 1 ? 2 : 1,
    }

    // 调用后端接口进行流程定义的操作（挂起或激活）
    const result: any = await operateActReProcdef(param)

    // 判断操作结果是否成功
    if (result.success && result.code === 200) {
      // 操作成功时，显示成功提示信息
      ElMessage({
        message: '操作成功',
        type: 'success',
      })

      // 初始化分页参数，设置当前页为第一页，每页显示 10 条数据
      currentPage.value = 1
      pageSize.value = 10

      // 初始化流程名称为空字符串
      processName.value = ''

      // 刷新页面数据以反映最新的操作结果
      getActReProcdefPageData()
    }
  }
  catch (error) {
    // 捕获异常并提取错误信息
    let errorMessage = '未知错误'
    if (error instanceof Error) {
      errorMessage = error.message
    }

    // 显示操作失败的错误提示信息
    ElMessage({
      message: `操作失败: ${errorMessage || '未知错误'}`,
      type: 'error',
    })
  }
}

/**
 * 异步函数：用于显示流程定义的图片
 * @param data ActReProcdefVO 类型的对象，包含流程定义的相关信息
 */
async function onShowImage(data: ActReProcdefVO) {
  try {
    // 组装查询参数，包括流程定义 ID
    const param: QueryImageOrXmlReq = {
      processDefinitionId: data.id,
    }

    // 调用后端接口获取流程定义的图片数据
    const result: any = await queryImage(param)

    // 判断查询结果是否成功
    if (result.success && result.code === 200) {
      // 如果成功，则更新流程定义的图片数据
      imageData.value = result.data

      // 打开图片对话框
      showImage.value = true
    }
    else {
      // 提示操作失败的错误提示信息
      ElMessage({
        message: `查询失败原因：${result.message}`,
        type: 'error',
      })
    }
  }
  catch (error) {
    // 捕获异常并提取错误信息
    let errorMessage = '未知错误'
    if (error instanceof Error) {
      errorMessage = error.message
    }

    // 显示操作失败的错误提示信息
    ElMessage({
      message: `查询失败: ${errorMessage || '未知错误'}`,
      type: 'error',
    })
  }
}

/**
 * 异步函数：用于显示流程定义的 XML 信息
 *
 * @param data ActReProcdefVO 类型的对象，包含流程定义的相关信息
 */
async function onShowXml(data: ActReProcdefVO) {
  try {
    // 组装查询参数，包括流程定义 ID
    const param: QueryImageOrXmlReq = {
      processDefinitionId: data.id,
    }

    // 调用后端接口获取流程定义的 XML 数据
    const result: any = await queryXml(param)

    // 判断查询结果是否成功
    if (result.success && result.code === 200) {
      // 第一步：URL 解码，得到 Base64
      const base64Str: string = decodeURIComponent(result.data)
      // 第二步：Base64 解码为二进制字符串
      const binaryStr = atob(base64Str)
      // 第三步：转为 UTF-8 文本
      const bytes = Uint8Array.from(binaryStr, c => c.charCodeAt(0))

      // 获取数据
      xmlData.value = new TextDecoder('utf-8').decode(bytes)

      // 打开 XML 对话框
      showXml.value = true
    }
    else {
      // 提示操作失败的错误提示信息
      ElMessage({
        message: `查询失败原因：${result.message}`,
      })
    }
  }
  catch (error) {
    // 捕获异常并提取错误信息
    let errorMessage = '未知错误'
    if (error instanceof Error) {
      errorMessage = error.message
    }

    // 显示操作失败的错误提示信息
    ElMessage({
      message: `查询失败: ${errorMessage || '未知错误'}`,
      type: 'error',
    })
  }
}

/**
 * 异步函数：在流程开始时调用
 * 该函数负责查询流程定义的动态参数，并在成功时显示流程启动对话框
 * @param data 流程定义的详细信息，用于获取流程定义ID
 */
async function onStart(data: ActReProcdefVO) {
  try {
    // 组装查询参数，包括流程定义 ID
    const param: QueryDynamicParametersReq = {
      processDefinitionId: data.id,
    }

    // 调用后端接口获取流程定义的动态参数
    const result: any = await queryDynamicParameters(param)

    // 判断查询结果是否成功
    if (result.success && result.code === 200) {
      // 如果成功，则更新流程定义的动态参数
      dynamicParametersData.value = result.data

      // 将数据放入 dynamicForm 表单数据中
      dynamicForm.data = dynamicParametersData.value

      // 收集流程部署定义ID
      processDefinitionId.value = data.id
    }

    // 打开 流程启动对话框
    showStart.value = true
  }
  catch (error) {
    // 捕获异常并提取错误信息
    let errorMessage = '未知错误'
    if (error instanceof Error) {
      errorMessage = error.message
    }

    // 显示操作失败的错误提示信息
    ElMessage({
      message: `查询流程动态参数失败: ${errorMessage || '未知错误'}`,
      type: 'error',
    })
  }
}

/**
 * 保存动态参数并启动流程的异步函数
 * 此函数首先验证动态表单的数据有效性，然后组装请求参数，最后调用后端接口启动流程
 */
async function onSaveDynamicParameters() {
  try {
    // 先执行表单校验成功才能执行后续保存操作
    await dynamicFormRef.value.validate()

    // 组装查询参数，包括流程定义 ID和动态参数
    const startProcessDynamicParametersReq = ref<StartProcessDynamicParametersReq[]>([])
    dynamicForm.data.forEach((item: DynamicParametersVO) => {
      startProcessDynamicParametersReq.value.push({
        dynamicVariableName: item.dynamicVariableName,
        dynamicVariableValue: item.dynamicVariableValue,
      })
    })
    const startProcessReq: StartProcessReq = {
      processDefinitionId: processDefinitionId.value,
      dynamicParameters: startProcessDynamicParametersReq.value,
    }

    // 调用后端接口启动流程实例
    const result: any = await startProcess(startProcessReq)

    // 判断执行结果是否成功
    if (result.success && result.code === 200) {
      // 如果成功，则更新流程定义的动态参数
      ElMessage({
        message: '启动流程成功',
        type: 'success',
      })

      // 关闭对话框
      showStart.value = false
    }
    else {
      // 提示操作失败的错误提示信息
      ElMessage({
        message: '启动流程失败',
        type: 'error',
      })
    }
  }
  catch (error) {
    // 捕获异常并提取错误信息
    let errorMessage = '请检查'
    if (error instanceof Error) {
      errorMessage = error.message
    }

    // 显示操作失败的错误提示信息
    ElMessage({
      message: `必填项校验失败: ${errorMessage || '未知错误'}`,
      type: 'error',
    })
  }
}
</script>

<template>
  <!-- 查询条件区域 -->
  <el-card style="height: 75px;">
    <el-form :inline="true" class="form-inline">
      <el-form-item label="流程定义名称">
        <el-input
          v-model.trim="processName"
          placeholder="请输入流程定义名称"
          maxlength="50"
          show-word-limit
        />
      </el-form-item>
      <el-form-item>
        <el-button v-hasButton="`btn.actReProcdef.page`" type="primary" icon="Search" @click="getActReProcdefPageData">
          查询
        </el-button>
        <el-button icon="Refresh" @click="resetQuery">
          重置
        </el-button>
      </el-form-item>
    </el-form>
  </el-card>

  <!-- 查询结果列表区域 -->
  <el-card style="margin: 10px 0px;">
    <el-table style="margin: 10px 0px;" :border="true" :data="actReProcdefList">
      <!-- ID 区域 -->
      <el-table-column type="selection" align="center" width="50px" />
      <!-- 表格数据 区域 -->
      <el-table-column
        v-for="(column, index) in tableColumns"
        :key="index"
        :type="column.type"
        :label="column.label"
        :prop="column.prop"
        :align="column.align"
        :width="column.width"
      >
        <!-- 使用单个 template 包裹所有条件 -->
        <template #default="scope">
          <!-- 翻译 suspensionState 状态 1 为 激活，2 为 挂起 -->
          <template v-if="column.label === '定义状态'">
            <template v-if="scope.row.suspensionState === 1">
              激活
            </template>
            <template v-else>
              挂起
            </template>
          </template>
          <!-- 判断是否是操作列 -->
          <div v-if="column.label === '操作'">
            <el-button v-hasButton="`btn.actReProcdef.operate`" type="primary" @click="onOperate(scope.row)">
              {{ scope.row.suspensionState === 1 ? '挂起' : '激活' }}
            </el-button>
            <el-button v-hasButton="`btn.actReProcdef.queryImage`" type="primary" @click="onShowImage(scope.row)">
              查看流程图
            </el-button>
            <el-button v-hasButton="`btn.actReProcdef.queryXml`" type="primary" @click="onShowXml(scope.row)">
              查看XML
            </el-button>
            <el-button v-hasButton="`btn.actReProcdef.queryDynamicParameters`" type="primary" @click="onStart(scope.row)">
              启动
            </el-button>
          </div>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页器 -->
    <el-pagination
      v-model:current-page="currentPage"
      v-model:page-size="pageSize"
      :page-sizes="[10, 20, 30, 40, 50]"
      layout="prev, pager, next, jumper,->, sizes, total"
      :total="Math.max(total, 0)"
      @current-change="getActReProcdefPageData"
      @size-change="handerPageData"
    />
  </el-card>

  <!-- 流程图展示弹出框 -->
  <el-dialog v-model="showImage" title="流程图展示" width="80%">
    <div class="image-container">
      <img :src="`data:image/jpeg;base64,${imageData}`" alt="流程图">
    </div>
  </el-dialog>

  <!-- XML 展示弹出框 -->
  <el-dialog v-model="showXml" title="XML 展示" width="30%">
    <div class="xml-container">
      <PrettyXml :xml="xmlData" :options="{ shortRecord: true }" />
    </div>
  </el-dialog>

  <!-- 启动流程 弹出框 -->
  <el-dialog v-model="showStart" title="启动流程" width="30%">
    <el-form ref="dynamicFormRef" :model="dynamicForm">
      <el-form-item
        v-for="(item, index) in dynamicForm.data"
        :key="index"
        :label="item.formName"
        :prop="`data.${index}.dynamicVariableValue`"
        :rules="{ required: true, message: '请选择选项', trigger: ['change', 'blur'] }"
      >
        <el-select
          v-model="item.dynamicVariableValue"
          multiple placeholder="请选择"
          :multiple-limit="item.selectLimit > 0 ? item.selectLimit : undefined"
        >
          <el-option
            v-for="one in item.selectData"
            :key="one.value"
            :label="one.label"
            :value="one.value"
          />
        </el-select>
      </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="showStart = false">
          取消
        </el-button>
        <el-button v-hasButton="`btn.actReProcdef.startProcess`" type="primary" @click="onSaveDynamicParameters">
          确认
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>

<style scoped>
.form-inline {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap; /* 适配小屏幕 */
}

.image-container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
}

.image-container img {
  max-width: 100%;
  max-height: 100%;
}

.xml-container {
  min-height: 300px;
  max-height: 600px;
  width: 100%;
  overflow-x: unset;
  overflow-y: scroll;
}
</style>
